OR-Tools CP-SAT 搜索参数
所有参数:
https://github.com/google/or-tools/blob/stable/ortools/sat/sat_parameters.proto
很多参数都要在了解背后的搜索算法后,才知道含义
输出所有参数及其默认值:
1 | attributes = [attr for attr in dir(solver.parameters) if not attr.startswith('__')] |
下面是参数类型及一些例子:
Branching and polarity 搜索算法相关参数
1 | enum VariableOrder { |
Conflict analysis 冲突分析相关参数
1 | enum ConflictMinimizationAlgorithm { |
Clause database management 子句数据管理
1 | # 触发清理,当可删除子句数量达到下面的配置 |
Variable and clause activities 变量子句活动设置
当每个冲突找到的时候,变量的活动开始增加
1 | variable_activity_decay = 0.8 |
Restart 算法重启相关配置
主要是配置在什么情况会重启算法
1 | enum RestartAlgorithm { |
Limits 搜索限制相关配置
1 | # 最大搜索时间,默认是不限制 |
Other 其他参数
1 | # 求解器随机数种子,可以考虑改变 |
Presolve 阶段相关参数
主要是在搜索前,对model做一些处理,有多种presolve策略(对变量、约束),以加速后续的搜索
Presolve参数过程可以参考:How the CP-SAT solver works
这个阶段对问题规模有一定要求,在较大规模问题下,开启这个应该有加速效果,在较小规模下开启应该会增加时间
1 | # 边界变量消除,变量x,not(x)出现的次数小于这个参数 |
设置cp_model_presolve = false后,速度提升比较明显,不会等待最长超时时间
在n_queens上试验,n=11, 相较开启presolve, 有1s左右的时间提升
Max-sat 参数
1 | # 是否使用一些提示/暗示来找到一个较好的初始解 |
Constraint programming 约束规划参数
1 | # 代表考虑的约束类型,匹配系统中应该设置为2 |
源码编译 & cython相关
环境:ubuntu 18.04 & Python 3.6
调用第三方求解器
OR-Tools中内置一些求解器,如MP Solver和CP-SAT solver。
但是OR-Tools实际上提供的是统一的求解器接口,内部连接的具体求解器我们可以配置,支持以下:
- SCIP 默认整合,无需手动安装
- Gurobi
- GLKP (Linux and MacOS only)
- CPLEX: IBM开发的一个商用求解器
调用第三方求解器时,需要单独安装第三方求解器,同时or-tools需要源码安装。
安装流程可以参考官方文档https://developers.google.com/optimization/install/python/source_linux中Build third parties,以及相关资料中的文档。
下载stable分支源码后,执行make third_party
报错如下:
移除makefiles/Makefile.third_party.unix.mk 210,283行-v参数,-v应该是输出,可能不支持了,移除应该不影响。
也可能是cmake版本问题.
第三方求解器源码编译时间较长(只是编译默认开源求解器scip、CBC等,添加其他可能时间更长)。
编译成功:
可行性较高。可以在不用修改原有代码的情况下,使用各种第三方求解器。
cython相关
1、源码编译or-tools
1 | make cc |
编译成功:
编译后的库文件:
2、编写增加约束cython代码,xx.pyx文件
定义包装器,用来桥接Python解释器到C++代码
需要对c++代码,函数有一定了解
用cython代码写添加约束的逻辑,生成函数
类似如下的函数。
1 | def MakeMatrixConstraint(solver, coefficients, lin_expr, double[:] lb, double[:] ub): |
3、编写setup.py函数,构建扩展模块
1 | from distutils.core import setup |
然后就可以在python代码中使用了。
可以尝试cython的方式,但是感觉比较麻烦。
其实可以考虑整数规划整体用C++实现,然后使用C++ grpc开放端口,供线上服务调用。
Tips
1. 减少变量数量 & 减少整数变量的域
2. 首选bool型而不是整数变量/约束
其实从源码来看差不多,bool型变量也是使用的IntVar,只是域是(0, 1)
其实定义bool变量是,可以直接使用IntVal(0, 1),但要考虑有些方法只针对bool型变量
1 | def NewBoolVar(self, name): |
3. 如何只寻找可行方案
1 | solver.SolveWithSolutionCallback() |
SearchForAllSolutions 有存储过程
4. 查看模型的一些信息
1 | print(model.ModelStats()) |
5. Parallelization(并行化)
可以使用多个线程并行运行求解器求解
1 | solver = cp_model.CpSolver() |
应该是前6个线程使用前6个搜索算法,其余的都是使用LNS,可能参数不同或者随机种子不同
- Default SAT Search
- Fixed search or LP Branching
- PSEUDO_COST_SEARCH (follow last best solution when branching)
- No linear relaxation (good for big models where CP-SAT propagation is enough)
- Max linear relaxation (gives good lower bounds)
- Core Based search
- LNS for remaining workers
搜索是独立的,存在解重复,可以尝试通过下面这个参数避免:
1 | interleave_search = True |
6. 日志开启
程序崩溃,或者搜索没有结果时,可以查看日志。
1 | log_search_progress = True |
可设置其他相关日志参数
和系统整合是,要考虑日志的兼容性
7. Solution hint / Warm start(解决方案提示/热启动)
如果我们在求解过程中加入一些变量的提示则可能加快求解速度。
1 | num_vals = 3 |
8. 单向约束
Implications方法是一种单向的约束操作:a => b (a,b均为布尔型变量) ,a为True则b也为True,反之则不成立
1 | model.AddImplication(a, b) |
9. 推荐使用字典来存储多索引变量
Storing Multi-index variables,便于理解
10. 强制所有变量都不相同约束
1 | model.AddAddAllDifferent(var_arr) |
11. 元素约束
1 | # target == var_arr[index] |
12. 优化目标,多目标优化
可以使用带权重的优化目标,比如5 a + 2 b -c,权重代表了优化目标的优先级顺序,权重越大则优先级越高。在使用最大化(最小化)优化时,希望其中的一个目标最小化(最大化),可以给该目标赋一个负值权重
此时尽量不要使用SearchForAllSolutions方法来搜索所有的解
1 | model.Minimize(5*a+2*b-c) |
13. Domain
在创建多个变量时,使用Domain应该比单独创建要快
1 | domain = cp_model.Domain(0, 10) |
相关资料
https://github.com/google/or-tools/blob/stable/ortools/sat/doc/model.md
https://www.jianshu.com/p/da3bf1b47681